package de.gameduell.net.src.communication {
	import de.gameduell.net.src.connection.PollConnection;	
	
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	import de.gameduell.framework.debug.Debug;
	import de.gameduell.net.src.message.IMessage;
	import de.gameduell.net.src.message.MessageEvent;
	import de.gameduell.net.src.protocol.CommunicationRequests;	

	public class MessageQueue extends EventDispatcher {

		private const MESSAGE_LIFETIME:Number = 6000;
		private const MAX_RETRIES:Number 	  = 3;
		
		private var queue:Array;
		private var messageLifetime:Timer;
		private var retries:int;
		private var lastMessage:IMessage;
		private var currMessageId:int;
		private var currConnectionType:String;
		private var _reconnecting:Boolean;

		public function MessageQueue() {
			queue = new Array();	
			currMessageId = 0;
			retries       = 0;
			_reconnecting = false;
			messageLifetime =  new Timer(MESSAGE_LIFETIME);
			messageLifetime.addEventListener(TimerEvent.TIMER, onMessageExpired);
		}
		
		public function close():void{
			messageLifetime.stop();
			messageLifetime.removeEventListener(TimerEvent.TIMER, onMessageExpired);
			queue = new Array();
		}
		
		public function addMessage(message:IMessage, connectionType:String):void{
			currConnectionType = connectionType;

			currMessageId++;
			switch(message.requestType){			
				case CommunicationRequests.SYNC:	
					addSync(message);
					break;
					
				case CommunicationRequests.SOCKET:
					addSocket(message);
					break;
							
				default:
					queue.push(message);
					if(queue.length == 1){	
						nextMessage();
					}
			}
		}

		private function addSync(message:IMessage):void {
			// handling for PollConnection
			if(currConnectionType == PollConnection.CONNECTION_TYPE){
				queue.push(message);
				if(queue.length == 1 || _reconnecting) {
					retries = _reconnecting ? retries : 0;
					_reconnecting = false;
					nextMessage();
				}
			} else { // handling for SocketConnection
				deleteMessagesType(CommunicationRequests.SYNC);
				var firstIsSocket:Boolean = false;
				if(queue.length > 0) {
					firstIsSocket = queue[0].requestType == CommunicationRequests.SOCKET;
				}
				if(firstIsSocket) {
					queue.splice(1, 0, message);
				}else {
					queue.unshift(message);
					if(queue.length == 1 || _reconnecting) {
						retries = _reconnecting ? retries : 0;
						_reconnecting = false;
						nextMessage();
					}
				}
				adjustMessageIds();
			}
		}

		private function addSocket(message:IMessage):void {
			deleteMessagesType(CommunicationRequests.SOCKET);
			deleteMessagesType(CommunicationRequests.SYNC);
			queue.unshift(message);
			nextMessage();
			adjustMessageIds();	
		}
		
		override public function toString():String{
			var result:String = "";
			for(var i:int=0;i< queue.length;i++){
				result += queue[i].requestType+", ";
			}
			return result;
		}
		
		private function deleteMessagesType(type:String):void{
			try{
				for(var i:* in queue){
					if(queue[i].requestType == type){
						queue.splice(i,1);
					}
				}
			}catch(e:Error){
				log("MessageQueue::deleteMessagesType: error: " + e, Debug.ERROR);
			}
		}

		private function adjustMessageIds():void {
			try{
				if(queue.length > 1) {	
					var firstMessage:IMessage = queue[0];
					var currId:int = firstMessage.id;
	
					for(var i:int = 1;i < queue.length;i++) {
						currId++;
						var currMessage:IMessage = queue[i];
						currMessage.id = currId;
					}
				}
				currMessageId = IMessage(queue[queue.length-1]).id;
			}catch(e:Error){
				log("MessageQueue::adjustMessageIds: error: " + e, Debug.ERROR);
			}
		}
		
		public function get nextMessageId():int{
			return currMessageId+1;
		}

		public function onMessageExpired(event:TimerEvent):void {
			messageLifetime.stop();
			if(retries < MAX_RETRIES){
				dispatchEvent(new MessageEvent(MessageEvent.MESSAGE_EXPIRED,queue[0]));
			}else{
				dispatchEvent(new MessageEvent(MessageEvent.MAX_RETRIES_REACHED,queue[0]));
			}
			retries++;
		}
		
		public function shift():IMessage{
			try{
				var deletedMessage:IMessage;
				for(var i:int = 0;i < queue.length; i++){
					if(queue[i] == lastMessage){
						deletedMessage = lastMessage;
						queue.splice(i,1);
						break;
					}
				}
			}catch(e:Error){
				log("MessageQueue::shift: " +e, Debug.ERROR);
			}
			messageLifetime.stop();
			nextMessage();
			return deletedMessage;
		}
		
		public function getLastMessage():IMessage{
			return lastMessage;
		}
		
		public function removeMessageById(aId:int):IMessage {
			var currMessage:IMessage = null;
			for(var i:int = 0; i < queue.length; i++){
				currMessage    = queue[i];
				if(currMessage.id == aId){
					currMessage = queue.splice(i,1)[0]; // grab the message
					break;
				}
			}
			return currMessage;
		}
		
		private function nextMessage():void{
			try{
				if(queue.length > 0){
					messageLifetime.start();
					lastMessage = queue[0];
					dispatchEvent(new MessageEvent(MessageEvent.NEXT_MESSAGE, lastMessage));
				}
			}catch(e:Error){
				log("nextMessage: " +e, Debug.ERROR);
			}
		}
		
		public function set reconnecting(isReconnection:Boolean):void {
			_reconnecting = isReconnection;
		}
		
		public function get size():int{
			return queue.length;
		}
		
		private function log(aMessage:String, logMode:String = Debug.DEBUG):void{
			if(Communication.DEBUG_ENABLED || logMode == Debug.ERROR){
				Debug.trace("MessageQueue::" +  aMessage, logMode);
			}
		} 
	}
}
